#pragma once

#include <osg/Referenced>
#include <osg/Vec3>
#include <osg\ref_ptr>
#include <osg\observer_ptr>
#include "corebase_global.h"
#include "odespacewrap.h"
#include "odeworldwrap.h"
#include "base.h"
#include <QVector>
#include <vector>
#include <ode/common.h>
#include <ode/collision_space.h>

namespace CoreBase
{
	class Transformable;

	class COREBASE_EXPORT ODEController : public osg::Referenced
	{
	public:
		ODEController(Base* msgSender = NULL);
		/** Overloaded constructor to supply a custom ODESpaceWrap and ODEWorldWrap.
		* @param spaceWrapper custom ODE space wrapper
		* @param worldWrap custom ODE world wrapper
		* @param msgSender : optional pointer to a class which will be used to send out
		*                the "collision" message.
		*/
		ODEController(ODESpaceWrap& spaceWrapper, ODEWorldWrap& worldWrap, Base* msgSender = NULL);
		/** Set the class that will act as the sender for the "collision" message.
		* @param msgSender : pointer to a class which will be used to send out
		*                    the "collision" message (can be NULL)
		*/
		void setMessageSender(Base* msgSender);
		/** Register a physics object with the system.  When added, the object
		* will be supplied to the underlying physics system for processing.
		* @param collidable A valid collision/physics object
		*/
		void registerCollidable(Transformable* collidable);
		/** Unregister a physics object with the system.  The object
		* will be removed from the underlying physics system.
		* @param collidable A valid collision/physics object
		*/
		void unRegisterCollidable(Transformable* collidable);
		/** Get the container of previously registered collidables.
		* @return The const container of registered collidables
		*/
		const QVector<Transformable*>& getRegisteredCollidables() const;
		/** Perform an iteration of the physics system.  Default implementation
		* will perform as many physics steps required to fulfill the supplied
		* time step.  Custom iteration algorithms can be created by deriving from
		* this class and overwriting this method.
		* @param deltaFrameTime The amount of time to simulate in the physics system.
		*/
		virtual void iterate(double deltaFrameTime);
		/**
		* Get the step size of the physics.  The physics will
		* be updated numerous times per frame based on this number.  For example,
		* if the delta frame rate is 33ms and the step size is 2ms, the physics
		* will be updated 16 times.
		* @return the step size in seconds
		* @see SetPhysicsStepSize()
		*/
		double getPhysicsStepSize() const;
		/// @see GetPhysicsStepSize()
		void setPhysicsStepSize(double stepSize = 0.0);
		///Set the gravity vector
		void setGravity(const osg::Vec3& gravity) const;
		///Get the gravity vector
		osg::Vec3 getGravity() const;
		///Get the ODE space ID
		dSpaceID getSpaceID() const;
		///Get the ODE world ID
		dWorldID getWorldID() const;
		///Supply a user-defined collision callback to replace the internal one
		void setUserCollisionCallback(dNearCallback* func, void* data=NULL) const;
		dNearCallback* getUserCollisionCallback() const;
		void* getUserCollisionData();
		const void* getUserCollisionData() const;
		///Get the ODE contact joint group ID
		dJointGroupID getContactJointGroupID() const;
		///Get a pointer to the internal ODEWorldWrap instance
		ODEWorldWrap* getWorldWrapper() const;
		///Get a pointer to the internal ODESpaceWrap instance
		ODESpaceWrap* getSpaceWrapper() const;
		/// @return the number of instances of this class that are holding a reference to the static ODE initialization.
		static unsigned getODERefCount();
	protected:
		virtual ~ODEController();
		///When two geoms collide, send out a "collide" message
		virtual void defaultCBFunc(ODESpaceWrap::CollisionData data);
		///perform one iteration using the supplied step size
		void step(double stepSize);
	private:
		void ctor();
		osg::ref_ptr<ODESpaceWrap>					_spaceWrapper;
		osg::ref_ptr<ODEWorldWrap>					_worldWrapper;
		///<The time (seconds) for the physics time step.
		///<(default = 0.0, indicating to use the System deltaFrameTime)
		double										_physicsStepSize;
		QVector<Transformable*>						_collidableContents;
		osg::observer_ptr<Base>						_msgSender;
	};
}